/**
 * Notes: 请求类封装
 * User: darcy
 * Author: 252452324@qq.com
 * Date: 2024/11/1
 * Time: 11:50
 * Version:v2.0
 */

import CryptoJS from 'crypto-js';
import User from '@/utils/user';


export default class Client {

	//配置信息
	private _config : Record<string, string>;

	/**
	 * 初始化构造函数
	 * @param sandbox 是否启用沙盒模式
	 * @param config 配置参数
	 */
	constructor(sandbox : boolean = false, config : Record<string, string> = {}) {
		let defaultConfig = {
			dev: { // 测试环境
				appId: '6ly7sa51xa5i',
				secretKey: '67XTz4El/eccq0gkNm/9vg==',
				baseCallUrl: "http://192.168.1.100:8787/app/appApi/",
			},
			prod: { // 生产环境(正式)
				appId: "anphCPb3wDjP",
				secretKey: "c1ksmn5g1uQJujTfQfofjw==",
				baseCallUrl: "http://v3.api-lease.niannianzu.com/app/zlApi/",
			}
		}

		this._config = sandbox ? defaultConfig.dev : defaultConfig.prod

		// 使用 Object.assign 合并配置对象，但确保 config 是一个有效的对象  
		if (Object.keys(config).length > 0) {
			this._config = { ...this._config, ...config };
		}
	}

	/**
	 * 获取配置信息
	 * @param name 获取配置相应对象下标的值
	 */
	getConfig(name ?: string) : Record<string, string> | string | null {
		let currentConfig = this._config;
		return name === undefined || name === '' ? { ...currentConfig } : currentConfig[name] || null;
	}

	/**
	 * 发起网络请求
	 */
	request(options) {
		return new Promise((resolve, reject) => {
			const _isShowToast = this.isShowToast(options.isShowToast ?? true);

			if (_isShowToast) {
				uni.showToast({
					title: '加载中',
					icon: 'loading',
				})
			}

			//请求参数
			let params = options.data || {};
			params = this.doFilterUndefinedAndNull(params);

			// 请求头部
			let header = {
				"Content-Type": "application/json;charset:utf-8"
			};

			//合并头部
			if (options.header && typeof options.header === 'object' && options.header !== null) {
				header = { ...header, ...options.header };
			}

			uni.request({
				url: options.url || this._config.baseCallUrl,
				method: options.method || 'POST',
				data: params,
				header: this.getHeader(header, params),
				success: (res) => {
					let resData = res.data
					if (resData['code'] === 200) {
						// 请求成功的信息
						resolve({ msg: resData?.msg || null, data: resData?.data || null })
					} else {
						// 错误回调
						this.errorCallback(resData);
					}
				},
				fail(err) {
					this.callbackNetworkShowModal()
					reject(err)
				},
				complete: () => {
					//隐藏loading
					if (_isShowToast) { uni.hideLoading() }
				},

			})

		})
	}

	/**
	 * 上传资源
	 */
	uploadFile(options) {
		return new Promise((resolve, reject) => {
			const _isShowToast = this.isShowToast(options.isShowToast ?? true);

			if (_isShowToast) {
				uni.showToast({
					title: '上传中，请稍等',
					icon: 'loading',
				})
			}
			
			//请求参数
			let params = options.data || {};
			params = this.doFilterUndefinedAndNull(params);
			
			uni.request({
				url: options.url || this._config.baseCallUrl,
				method: options.method || 'POST',
				data: params,
				header: this.getHeader(options.header || {}, params),
				success: (res) => {
					let resData = res.data
					if (resData['code'] === 200) {
						// 请求成功的信息
						resolve({ msg: resData?.msg || null, data: resData?.data || null })
					} else {
						// 错误回调
						this.errorCallback(resData);
					}
				},
				fail(err) {
					this.callbackNetworkShowModal()
					reject(err)
				},
				complete: () => {
					//隐藏loading
					if (_isShowToast) { uni.hideLoading() }
				},
				
			})
		})
	}

	/**
	 * 获取当前时间戳（秒）
	 */
	private getTimestamp() : number {
		return Math.floor(Date.now() / 1000);
	}

	/**
	 * 判断消息提示框
	 */
	private isShowToast(state : boolean) : boolean {
		return state === true;
	}

	/**
	 * 获取请求头部(封装)
	 */
	private getHeader(headerData : Record<string, string> = {}, params : Record<string, string> = {}) {
		if (!this._config || typeof this._config !== 'object' || this._config.appId === undefined || this._config.secretKey === undefined) {
			throw new Error('配置信息不完整或无效');
		}

		//当前时间戳
		const timestamp = this.getTimestamp();

		let hashData = timestamp + "\n" + this._config.secretKey + "\n";

		//如递交参数，则跟参数一起加签
		if (Object.keys(params).length > 0) {

			// 获取对象的键并排序(升序)
			const sortedKeys = Object.keys(params).sort();

			// 使用 URLSearchParams 来构建查询字符串  
			const queryString = new URLSearchParams();
			sortedKeys.forEach(key => {
				const value = params[key];
				queryString.append(key, value.toString());
			});

			//参数拼接参数(a=1&c=foo&d=false)后进行MD5加密
			hashData = hashData + CryptoJS.MD5(queryString.toString());
		}

		//使用crypto-js进行HMAC哈希计算
		let hash = CryptoJS.HmacSHA256(hashData, this._config.secretKey).toString(CryptoJS.enc.Base64);

		let userData = new User();

		let header = {
			"X-Open-App-Id": this._config.appId,
			"X-Open-Sign": hash,
			"X-Open-Timestamp": timestamp,
			"X-Token": userData.get('token') || ''
		};

		if (Object.keys(headerData).length > 0) {
			header = { ...header, ...headerData }
		}

		return header
	}

	/**
	 * 执行过滤器 undefined 及 null 的值
	 */
	private doFilterUndefinedAndNull<T>(obj : T) : { [P in keyof T]: Exclude<T[P], undefined | null> } {
		const result : any = {};
		const timestamp = this.getTimestamp();

		obj = { ...{ timestamp }, ...obj }

		for (const key in obj) {
			if (obj.hasOwnProperty(key) && obj[key] !== undefined && obj[key] !== null) {
				result[key] = obj[key];
			}
		}
		return result as { [P in keyof T]: Exclude<T[P], undefined | null> };
	}

	/**
	 * 错误回调
	 */
	private errorCallback(resData) {
		// 转跳登陆
		if (resData.code === 4000) {
			uni.showToast({
				title: "请登录",
				icon: 'none',
				duration: 1500
			});

			// 移除用户数据
			let user = new User()
			user.remove();

			//跳转
			uni.navigateTo({
				url: '/pages/login/login'
			})
		} else {

			//其它类型错误
			uni.showToast({
				title: resData.msg,
				icon: 'none',
				duration: 1500
			});
		}
	}

	/**
	 * 网络交互回调
	 */
	private callbackNetworkShowModal() {
		uni.getNetworkType({
			success: (res) => {
				if (res.networkType === null || res.networkType === '') {
					uni.showModal({
						content: '似乎网络有问题，稍后重试',
						showCancel: false
					})
				}
			},
			fail() {
				uni.showModal({
					content: '似乎网络有问题，稍后重试',
					showCancel: false
				})
			}
		});
	}
}